from flask import Flask, jsonify, request, render_template,session
import modules.net.pynet
import dto.transaction
import util.chash
import util.ctime
import util.ccrypto
import modules.script
import modules.appconfig
import modules.db
import dto.utx
import modules.log
import webapp.webapputil
import json
import copy
from util.singelMode import singleton 
@singleton
class CTransaction:
    def __init__(self):
        self.current_transactions = []
        self.__attachApi()
        pass
    def __call__(self):
        return self
    
    def ifIsAlreadyUsedInTxsPool(self,outPoint):
        for tx in self.current_transactions:
            for txin in tx['txins']:
                if outPoint['txhash']==txin['prevout']['txhash'] and outPoint['outindex']==txin['prevout']['outindex']:
                    return True
        return False
    def ifIsAlreadyUsedInThefivePreBlockDequeFromTheTipBlk(self,outPoint):
        t = len(modules.blockchain.CBlockchain.fivePreBlockhashDequeFromTheTipBlk)
        while t>0:
            blockhash=modules.blockchain.CBlockchain.fivePreBlockhashDequeFromTheTipBlk[t-1]
            block = modules.block.CBlock.getBlockFromDiskByhash(blockhash)
            for tx in block.transactions:
                for txin in tx['txins']:
                    if outPoint['txhash']==txin['prevout']['txhash'] and outPoint['outindex']==txin['prevout']['outindex']:
                        return True
            t=t-1
        return False

    def removalTxInTxsPool(self,txlist):
        for tx in txlist:
            if tx in self.current_transactions:
                index = self.current_transactions.index(tx)
                del self.current_transactions[index]
    def addCopyRightToChain(self,privatekey,recipient,content):
        coinbaseTx= dto.transaction.CTransaction(txtype=2)
        txIns=[]
        txouts=[]

            
        coutP = dto.transaction.COutPoint("The is a transanction which add the new copyright to the chain",-1)
        coutPStr = coutP.serializeToJsonStr()
        signatureStr, publickey=util.ccrypto.CCrypto.signature(coutPStr,privatekey)
        scriptSig=modules.script.CScriptSig(signatureStr, publickey)
        txIn0 = dto.transaction.CTxIn(coutP.serializeToPyObject(),scriptSig.serializeToPyObject())

        txIns.append(txIn0.serializeToPyObject())
        
        coinbaseTx.txins=txIns
        
        scriptPubKey = modules.script.CScriptPubKey(recipient)
        
        contentJsonStr,contentHash=util.chash.CHash.getCopyRightContentHash(content)
        txout0 = dto.transaction.CTxOut(contentHash,contentJsonStr,scriptPubKey.serializeToPyObject()) 

        txouts.append(txout0.serializeToPyObject())
        coinbaseTx.txouts=txouts

        coinbaseTx.timestamp = str(util.ctime.Ctime().getNowTimestamp())
        txhash=util.chash.CHash.getTransactionHash(coinbaseTx.serializeToPyObject())
        coinbaseTx.selfhash = txhash
        
        self.addUnconfirmedTransactinon(coinbaseTx.serializeToPyObject())
        result={
            'message':'the transaction is success recieved',
            'copyrightID':contentHash,
            'txhash':txhash,
        }
        flag=coinbaseTx.isAddCopyRightToTheChainTx()
        return result

    
        
    
    def findUTXO(self,address):
        UTXOs = []
        unspentTransactions = self.findUnspentTransactions(address)
        for tx in unspentTransactions:
             for out in tx["unSpendOuts"]:
                 scriptPubkey = modules.script.CScriptPubKey.deserializeFromPyObject(out['txout']["scriptPubKey"])
                 if scriptPubkey.isBelongTothisAddress(address):
                     UTXOs.append(out)
        return UTXOs
    
    def getcopyrightListByaddress(self,address):
        copyrightList = []
        UTXOs = self.findUTXO(address)
        for out in UTXOs:
            a={
                'copyrightID':out['txout']["value"],
                'copyrightContent':out['txout']["content"]
            }
            copyrightList.append(a)
        return copyrightList
        
    def getcopyrightContentBycopyrightID(self,copyrightID):
        db = modules.db.CDb.db["chainStateFile"]
        for key, value in db:
                utx = dto.utx.CUTx.deserializeFromJsonStr(value.decode('utf-8'))
                for unspendOut in utx.unSpendOuts:
                    if copyrightID==unspendOut['txout']['value']:
                        return json.loads(unspendOut['txout']['content'])
        return ""


                    

    #当前所有的拥有的版权
    def findUnspentTransactions(self,address):
        unspentTXs=[]
        db = modules.db.CDb.db["chainStateFile"]
        for key, value in db:
                utx = dto.utx.CUTx.deserializeFromJsonStr(value.decode('utf-8'))
                for unspendOut in utx.unSpendOuts:
                    scriptPubkey = modules.script.CScriptPubKey.deserializeFromPyObject(unspendOut['txout']["scriptPubKey"])
                    if  scriptPubkey.isBelongTothisAddress(address):
                        outPoint={
                            'txhash': key.decode('utf-8'),
                            'outindex': unspendOut["outindex"]
                        }
                        if self.ifIsAlreadyUsedInTxsPool(outPoint):
                            modules.log.Clog.logger.error("已经在交易池中使用过了")
                        elif self.ifIsAlreadyUsedInThefivePreBlockDequeFromTheTipBlk(outPoint):
                            modules.log.Clog.logger.error("已经在主链中使用过了")
                        else:
                            unspentTXs.append(utx.serializeToPyObject())
        return unspentTXs
        
    #用于当一个新的交易创建的时候,找到所需数量的输出
    def findSpendableOutputs(self,sender,copyrightID):
        # unspentOutputs ={}
        unspentTXs = self.findUnspentTransactions(sender)
       
        for tx in unspentTXs:
            txhash =tx["txhash"]
            for out in tx["unSpendOuts"]:
               scriptPubkey = modules.script.CScriptPubKey.deserializeFromPyObject(out['txout']["scriptPubKey"])
               if scriptPubkey.isBelongTothisAddress(sender) and out['txout']["value"]==copyrightID:
                   result={
                       'txhash':txhash,
                       'txtype':tx['txtype'],
                       'copyrightContent':out['txout']['content']
                   }
                   return result

        return ""

    def generateAuthorizeTranscation(self,privatekey,sender,recipient,copyrightID,user):
        txInputs = []
        txOutputs = []
        #1.获取相关地址下未花费的Outputs
        
        reuslt= self.findSpendableOutputs(sender,copyrightID)
        if reuslt=="":
            return "copyrightID is not exit ,please change the address and copyrightID"
        if reuslt['txtype'] ==1 or reuslt['txtype'] ==2 or reuslt['txtype'] ==3:
            
            txhash=reuslt['txhash']
            copyrightContent=reuslt['copyrightContent']

            #2.通过遍历Outputs,构造输入
            outIndex=0
        
                
            coutP = dto.transaction.COutPoint(txhash,outIndex)
            coutPStr = coutP.serializeToJsonStr()
            signatureStr, publickey=util.ccrypto.CCrypto.signature(coutPStr,privatekey)
            scriptSig=modules.script.CScriptSig(signatureStr, publickey)
            
            txIn = dto.transaction.CTxIn(coutP.serializeToPyObject(),scriptSig.serializeToPyObject())
            txInputs.append(txIn)

        
            copyrightContent=json.loads(copyrightContent)
            copyrightContent['user']=user
            
            contentJsonStr,contentHash=util.chash.CHash.getCopyRightContentHash(copyrightContent)
            scriptPubKey = modules.script.CScriptPubKey(recipient)
            txout = dto.transaction.CTxOut(contentHash,contentJsonStr,scriptPubKey.serializeToPyObject())  
            txOutputs.append(txout)

            #4.构造一个tx
            newtransaction= dto.transaction.CTransaction(txtype=3)
            newtransaction.txins=txInputs
            newtransaction.txouts=txOutputs

            newtransaction.timestamp = util.ctime.Ctime().getNowTimestamp()
            newtransactionTxHash = util.chash.CHash.getTransactionHash(newtransaction.serializeToPyObject())

            newtransaction.selfhash = newtransactionTxHash
            
            return   newtransaction  
        else:
            return "the copycoent about copyrightID is not right "
    #依据提供的数据本地构造一个tx
    def generateTranscation(self,privatekey,sender,recipient,copyrightID,newown):
                
        txInputs = []
        txOutputs = []
        #1.获取相关地址下未花费的Outputs
        reuslt= self.findSpendableOutputs(sender,copyrightID)
        if reuslt=="":
            return "copyrightID is not exit ,please change the address and copyrightID"
        if reuslt['txtype'] ==1 or reuslt['txtype'] ==2:
            
            txhash=reuslt['txhash']
            copyrightContent=reuslt['copyrightContent']

            #2.通过遍历Outputs,构造输入
            outIndex=0
        
                
            coutP = dto.transaction.COutPoint(txhash,outIndex)
            coutPStr = coutP.serializeToJsonStr()
            signatureStr, publickey=util.ccrypto.CCrypto.signature(coutPStr,privatekey)
            scriptSig=modules.script.CScriptSig(signatureStr, publickey)
            
            txIn = dto.transaction.CTxIn(coutP.serializeToPyObject(),scriptSig.serializeToPyObject())
            txInputs.append(txIn)

        
            copyrightContent=json.loads(copyrightContent)
            copyrightContent['own']=newown
            
            contentJsonStr,contentHash=util.chash.CHash.getCopyRightContentHash(copyrightContent)
            scriptPubKey = modules.script.CScriptPubKey(recipient)
            txout = dto.transaction.CTxOut(contentHash,contentJsonStr,scriptPubKey.serializeToPyObject())  
            txOutputs.append(txout)

            #4.构造一个tx
            newtransaction= dto.transaction.CTransaction()
            newtransaction.txins=txInputs
            newtransaction.txouts=txOutputs

            newtransaction.timestamp = util.ctime.Ctime().getNowTimestamp()
            newtransactionTxHash = util.chash.CHash.getTransactionHash(newtransaction.serializeToPyObject())

            newtransaction.selfhash = newtransactionTxHash
            
            return   newtransaction  
        else:
            return "the copycoent about copyrightID is not right "


    def validTranscation(self,tx):
        '''
        @tx  python object dict
        '''
        tx=copy.deepcopy(tx)
       
        isvalid=dto.transaction.CTransaction.valid(tx)
        return isvalid
       
         
     

    #处理本地产生的tx,以及处理从网络中其它节点广播过来的tx
    def receiveTransacton(self,transaction,fromNodeAddress=""):
        """
        transaction 为python dict obect
        """
       
        return  self.processUnconfirmedTransaction(transaction,fromNodeAddress)
    
    #对接受到的tx的合法性进行验证比如：判断是否双花费、验证提供的数据是否伪造
    def processUnconfirmedTransaction(self, tx,fromNodeAddress=""):
        """
        验证tx数据的合法性
        """
        if self.validTranscation(tx):
            #如果接受的tx合法、则将该tx添加到交易池中等待打包到区块中
            return self.addUnconfirmedTransactinon(tx,fromNodeAddress)
        else:
            return False  
  

    
    def addUnconfirmedTransactinon(self,tx,fromNodeAddress=""):
        """
        tx 为python object
        1.把经过验证的tx添加到交易内存池，等待打包到区块中
        2.将tx广播到网络中
        """

        #添加到交易池中
        self.current_transactions.append(tx)
        tx1 = dto.transaction.CTransaction.deserializeFromPyObject(tx)
        #广播交易
        response=modules.net.pynet.CP2pnet.broadcast("tx",tx,fromNodeAddress)
        return True
    @staticmethod
    def updatetotalstransactionnumber(value,updatetype=1):
        totaltxnumbers=modules.db.CDb.getValueByKey("chainIndexFile","totaltxnumbers")
        if totaltxnumbers!=None:
            totaltxnumbers=int(totaltxnumbers)
            if updatetype==1:
                value=totaltxnumbers+value
            else:
                value=totaltxnumbers-value
        if value!=0:
            modules.db.CDb.setValue("chainIndexFile","totaltxnumbers",str(value))
    @staticmethod
    def gettotalstransactionnumber():
        totaltxnumbers=modules.db.CDb.getValueByKey("chainIndexFile","totaltxnumbers")
        if totaltxnumbers!=None:
            return int(totaltxnumbers)
        else:
            return 0
    @staticmethod
    def updatetotalstransactionnumber1(fromblockhash,endblockhash):
        blkhash = fromblockhash
        storeInfo=modules.db.CDb.getValueByKey("chainIndexFile",blkhash).split(',')
         
        value=int(storeInfo[2])
        while blkhash!= endblockhash:
            modules.transaction.CTransaction.updatetotalstransactionnumber(value,2)
            storeInfo=modules.db.CDb.getValueByKey("chainIndexFile",blkhash).split(',')
            blkhash=storeInfo[1]
            value=int(storeInfo[2])
        
    def __attachApi(self):
        @modules.net.pynet.CP2pnet.severapp.route('/copyright/add',methods=['POST'])
        def newcopyright():

            values = request.get_json()
            if values!=None:
                required = ['privatekey', 'recieveaddress', 'copyrightcontent']
                if not all(k in values for k in required):
                    return 'Missing values', 400
                copyrightcontent=values['copyrightcontent']
                privatekey=values['privatekey']
                recieveaddress=values['recieveaddress']
                required=['copyrightName','workType','author','own','place','finishDate','describe','effectiveDate','periodOfvalidity']
                if not all(k in values['copyrightcontent'] for k in required):
                    return 'copyrightcontent Missing values', 400
                if not util.ccrypto.CCrypto.ifIsRightPrivateKeyString(privatekey):
                    return 'privatekey is not right', 400
                address=util.ccrypto.CCrypto.getAddressFromKeystr(privatekey)
                if not modules.blockchain.CBlockchain.ifIsThecopyrightmanageraccount(address):
                    return 'privatekey is not the manager', 400
                reuslt=self.addCopyRightToChain(privatekey,recieveaddress,copyrightcontent)
                return jsonify(reuslt), 200
            return 'request data is not josn format', 401 

        @modules.net.pynet.CP2pnet.severapp.route('/copyright/transfer',methods=['POST'])
        def copyrighttransfer():
            values = request.get_json()
            if values!=None:
                required = ['privatekey', 'recipient', 'copyrightID','newown']
                if not all(k in values for k in required):
                    return 'Missing values', 400
                # Create a new Transaction
                privatekey=values['privatekey']

                if util.ccrypto.CCrypto.ifIsRightPrivateKeyString(privatekey):
                    sender=util.ccrypto.CCrypto.getAddressFromKeystr(privatekey)
                    tx = self.generateTranscation(privatekey,sender, values['recipient'], values['copyrightID'],values['newown'])
                    if isinstance(tx,str):
                        response = {
                            'message': str(tx),
                            'status':501
                                }
                        return jsonify(response), 200
                    else:
                        isaddToMeool= self.receiveTransacton(tx.serializeToPyObject())
                        if isaddToMeool:
                            response = {
                                'message': f'Transaction will be added to Block ',
                                'txhash':tx.selfhash,
                                'sender':sender, 
                                'recipient':values['recipient'],
                                'copyrightID':tx.txouts[0].value,
                                'status':500
                                }
                            return jsonify(response), 200
                        else:
                            response = {
                                'message': 'values is not valit',
                                'status':502
                                }
                            return jsonify(response), 200    
                else:
                    response = {
                                'message': f'privatekey is not right',
                                'status':503
                                }
                    return jsonify(response), 200
            else:
                return 'request data is not josn format', 401 
        

        @modules.net.pynet.CP2pnet.severapp.route('/copyright/authorization',methods=['POST'])
        def copyrightauthorization():
            values = request.get_json()
            if values!=None:
                required = ['privatekey', 'recipient', 'copyrightID','user']
                if not all(k in values for k in required):
                    return 'Missing values', 400
                # Create a new Transaction
                privatekey=values['privatekey']

                if util.ccrypto.CCrypto.ifIsRightPrivateKeyString(privatekey):
                    sender=util.ccrypto.CCrypto.getAddressFromKeystr(privatekey)
                    tx = self.generateAuthorizeTranscation(privatekey,sender, values['recipient'], values['copyrightID'],values['user'])
                    if isinstance(tx,str):
                        response = {
                            'message': str(tx),
                            'status':501
                                }
                        return jsonify(response), 200
                    else:
                        isaddToMeool= self.receiveTransacton(tx.serializeToPyObject())
                        if isaddToMeool:
                            response = {
                                'message': f'Transaction will be added to Block ',
                                'txhash':tx.selfhash,
                                'sender':sender, 
                                'recipient':values['recipient'],
                                'copyrightID':tx.txouts[0].value,
                                'status':500
                                }
                            return jsonify(response), 200
                        else:
                            response = {
                                'message': 'values is not valit',
                                'status':502
                                }
                            return jsonify(response), 200    
                else:
                    response = {
                                'message': f'privatekey is not right',
                                'status':503
                                }
                    return jsonify(response), 200
            else:
                return 'request data is not josn format', 401 

      

       



        #接受从网络中传来的transaction
        @modules.net.pynet.CP2pnet.severapp.route('/transaction',methods=['POST'])
        def recieveTransactionFromNet():
            values = request.get_json()
            if values!=None:
                remote_addr = values['addrFrom']
                tx=values['data']
                # remote_addr = ""
                # tx=values
                isaddToMeool= self.receiveTransacton(tx,remote_addr)
                if isaddToMeool:
                    response = {'message': f'Transaction is recived '}
                    return jsonify(response), 201
                else:
                    response = {'message': f'values is not valit'}
                    return jsonify(response), 401  
            else:
                return 'request data is not josn format', 401 
        

    
     
        
        
    

               